<?php
/**
 * Created by PhpStorm.
 * User: 44766
 * Date: 2018-12-03
 * Time: 11:09
 */

namespace App\Services\LbsService;


use GeoHash;

class LbsService
{
    const SOSO_API        = "https://apis.map.qq.com";
    const SOSO_KEY        = "HTLBZ-H6K3D-G7H4S-PK7HC-4M7JS-ARBJH";
    const SOSO_SECRET_KEY = "4oAuaypT3POyTAa5LnzoujddyOtvqvuF";

    protected $point;
    protected $point_geo;

    /**
     * @param $point
     * @param null $lng
     * @return LbsService
     */
    public function point($point, $lng = null)
    {
        try {
            $this->point = $this->changeToPoint($point, $lng);
        } catch (\Exception $e) {
        }
        return $this;
    }

    public function geo($point)
    {
        try {
            $this->point = $this->changeToPoint($point);
        } catch (\Exception $e) {
        }
        return $this;
    }

    public function toGeo()
    {
        return GeoHash::encode($this->point[0], $this->point[1]);
    }

    public function toPoint()
    {
        return $this->point;
    }

    public function address($address)
    {
        $address = str_replace(['#', '?',' '], ['', '',''], $address);
        $path    = '/ws/geocoder/v1?address=' . $address . '&key=' . self::SOSO_KEY;
        $url     = self::SOSO_API . $path . '&sig=' . $this->getSign($path);
        $result  = json_decode(file_get_contents($url), true);
        if ($result['status'] == '0') {
            $this->point = [$result['result']['location']['lat'], $result['result']['location']['lng']];
            return $this;
        }
        throw new \Exception('[' . $result['message'] . ']:' . $address);
//        失败返回
//        $result = [
//            "status"  => 111,
//            "message" => "签名验证失败"
//        ];
    }

    public function toAddress($all = false)
    {
        $path   = '/ws/geocoder/v1?key=' . self::SOSO_KEY . '&location=' . $this->point[0] . ',' . $this->point[1];
        $url    = self::SOSO_API . $path . '&sig=' . $this->getSign($path);
        $result = json_decode(file_get_contents($url), true);
        if ($result['status'] == '0') {
            if ($all) {
                return $result['result'];
            }
            if (isset($result['result']['formatted_addresses']['recommend'])) {
                return $result['result']['formatted_addresses']['recommend'];
            }
            return $result['result']['address'];
        }
//        失败返回
//        $result = [
//            "status"  => 111,
//            "message" => "签名验证失败"
//        ];
        return false;
    }

    public function around($precision)
    {
        $geo = $this->toGeo();
        return substr($geo, 0, $precision);
    }

    /**
     * @param $point
     * @param int $decimal
     * @return array
     */
    public function distance($point, $decimal = 2)
    {
        try {
            $point = $this->changeToPoint($point);
        } catch (\Exception $e) {
        }

        $EARTH_RADIUS = 6370.996; // 地球半径系数
        $PI           = 3.1415926;

        $radLat1 = $this->point[0] * $PI / 180.0;
        $radLat2 = $point[0] * $PI / 180.0;

        $radLng1 = $this->point[1] * $PI / 180.0;
        $radLng2 = $point[1] * $PI / 180.0;

        $a = $radLat1 - $radLat2;
        $b = $radLng1 - $radLng2;

        $distance = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2)));
        $distance = $distance * $EARTH_RADIUS * 1000;


        $distance = $distance / 1000;

        return round($distance, $decimal);
    }

    /**
     * @param $point
     * @param null $lng
     * @return array|int|string
     * @throws \Exception
     */
    public function changeToPoint($point, $lng = null)
    {
        if (!is_array($point)) {
            if ($lng) {
                $point = [$point, $lng];
            } elseif (is_numeric($point)) {
                list($lat, $lng) = GeoHash::decode($point);
                $point = [$lat, $lng];
            } else {
                //todo
                throw new \Exception('');
            }
        }
        return $point;
    }

    private function getSign($path)
    {
        return md5($path . self::SOSO_SECRET_KEY);
    }
}